package com.tianji.promotion.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.ExchangeCode;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.enums.ExchangeCodeStatus;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.mapper.UserCouponMapper;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import com.tianji.promotion.utils.CodeUtil;
import com.tianji.promotion.utils.MyLock;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

/**
 * <p>
 * 用户领取优惠券的记录，是真正使用的优惠券信息 服务实现类
 * </p>
 *
 * @author wxh
 * @since 2024-09-03
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements IUserCouponService {
    private final CouponMapper couponMapper;
    private final IExchangeCodeService codeService;
    private final StringRedisTemplate redisTemplate;
    private final RedissonClient redissonClient;

    /**
     * 用户手动领取优惠券
     *
     * @param couponId 优惠券Id
     */
    @Override
    public void addCoupon(Long couponId) {
        //0获取userId
        Long userId = UserContext.getUser();
        //1.查询优惠券信息
        Coupon coupon = couponMapper.selectById(couponId);
        //1.1根据userId查询我的优惠券信息
        //收集优惠券的数量
        Integer count = this.lambdaQuery()
                .eq(UserCoupon::getUserId, userId)
                .eq(UserCoupon::getCouponId, couponId).count();
        //2.判断是否存在
        if (coupon == null) {
            throw new BizIllegalException("该优惠券不存在");
        }
        //3.库存是否充足
        if (coupon.getIssueNum() >= coupon.getTotalNum()) {
            throw new BizIllegalException("优惠券库存不足");
        }
        //4.是否超出每人限领数量
        if (count >= coupon.getUserLimit()) {
            throw new BizIllegalException("已达领取上限");
        }
        //8.调用addCouponAndUpdateCouponCount方法
        //版本一（解决不了集群模式下分布式锁问题）
           /* synchronized (userId.toString().intern()) {
                UserCouponServiceImpl userCouponService = (UserCouponServiceImpl) AopContext.currentProxy();
                userCouponService.addCouponAndUpdateCouponCount(userId, coupon);

            }*/
        //版本二 (使用简单的分布式锁 setNX)
        //设置key，用用户id作为标识
            /*String key = LOCK_COUPON_UID + userId;
            //获取锁id
            String lockId = String.valueOf(Thread.currentThread().getId());
            //1.创建锁对象锁
            MyLock myLock = new MyLock(key, redisTemplate);
            try {
                //2.尝试获取锁（注册锁|设置锁）
                boolean isLock = myLock.tryLock(lockId, 5L, TimeUnit.SECONDS);
                if (!isLock) {
                    throw new BizIllegalException("请求太频繁");
                }
                //使用动态代理类里面的有事务方法
                UserCouponServiceImpl userCouponService = (UserCouponServiceImpl) AopContext.currentProxy();
                userCouponService.addCouponAndUpdateCouponCount(userId, coupon);
            } finally {
                //3锁删除
                myLock.unLock(lockId);
            }*/

        //版本三  使用redission 获取锁
        /*String key = LOCK_COUPON_UID + userId;
        //获取锁
        RLock lock = redissonClient.getLock(key);
        boolean isLock = lock.tryLock();
        if (!isLock) {
            throw new BizIllegalException("请求太频繁");
        }
        try {
            //使用动态代理类里面的有事务方法
            UserCouponServiceImpl userCouponService = (UserCouponServiceImpl) AopContext.currentProxy();
            userCouponService.addCouponAndUpdateCouponCount(userId, coupon);
            //演示看门狗机制
            *//*try {
                Thread.sleep(35000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*//*
        } finally {
            //删除锁
            lock.unlock();
        }*/

        //版本四
        //使用动态代理类里面的有事务方法
        UserCouponServiceImpl userCouponService = (UserCouponServiceImpl) AopContext.currentProxy();
        userCouponService.addCouponAndUpdateCouponCount(userId, coupon);
    }

    /**
     * 新增我的优惠券和修改优惠券的已领取数量
     *
     * @param userId 用户id
     * @param coupon 查出来的优惠券实体
     */
    @MyLock(name = "lock:coupon")
    @Transactional
    public void addCouponAndUpdateCouponCount(Long userId, Coupon coupon) {
        //5.优惠券已发放数量加1
        couponMapper.updateCouponCount(coupon.getId());
        //6.获取优惠券的开始和过期时间
        LocalDateTime begin = coupon.getIssueBeginTime();
        LocalDateTime end = coupon.getIssueEndTime();
        //6.1没有开始时间则为有效天数
        if (ObjectUtil.isEmpty(begin)) {
            begin = LocalDateTime.now();
            end = LocalDateTime.now().plusDays(coupon.getTermDays());
        }
        //7.生成优惠券
        UserCoupon userCoupon = new UserCoupon();
        userCoupon.setUserId(userId);
        userCoupon.setCouponId(coupon.getId());
        userCoupon.setTermBeginTime(begin);
        userCoupon.setTermEndTime(end);
        save(userCoupon);
    }

    /**
     * 兑换码兑换优惠券
     *
     * @param code
     */
    @Override
    public void addExchange(String code) {
        //0获取userId
        Long userId = UserContext.getUser();
        //1.解析兑换码拿到自增序列号
        long serialNum = CodeUtil.parseCode(code);
        //2.直接通过setBit命令返回的值来判断是否兑换过
        boolean exsit = codeService.UpdateExchangeStyatus(serialNum, true);
        //
        if (exsit) {
            throw new BizIllegalException("该兑换码已经兑换过，不能重复兑换");
        }
        try {
            //4根据序列号查询兑换码
            ExchangeCode exchangeCode = codeService.getById(serialNum);
            //5.判断是否存在
            if (ObjectUtil.isEmpty(exchangeCode)) {
                throw new BizIllegalException("兑换码不存在");
            }
            LocalDateTime now = LocalDateTime.now();
            //6.判断是否过期
            if (now.isAfter(exchangeCode.getExpiredTime())) {
                throw new BizIllegalException("兑换码已过期");
            }
            //查询优惠券的限领数量
            Coupon coupon = couponMapper.selectById(exchangeCode.getExchangeTargetId());
            //查询我的兑换码数量
            Integer count = lambdaQuery().eq(UserCoupon::getUserId, userId)
                    .eq(UserCoupon::getId, serialNum).count();
            //7.判断是否达到领取上限
            if (count >= coupon.getUserLimit()) {
                throw new BizIllegalException("已达领取上限");
            }
            //8.调用addCouponAndUpdateCouponCount方法
            addCouponAndUpdateCouponCount(userId, coupon);
            //9更新兑换码状态
            this.lambdaUpdate()
                    .eq(UserCoupon::getUserId, userId)
                    .eq(UserCoupon::getId, serialNum)
                    .set(UserCoupon::getStatus, ExchangeCodeStatus.USED)
                    .update();
        } catch (BizIllegalException e) {
            //执行代码出现一次重置兑换次数
            codeService.UpdateExchangeStyatus(serialNum, false);
            throw e;
        }
    }
}
